import { PAPU } from ".";

export class ChannelNoise {
    private papu: PAPU;

    public isEnabled: boolean = null;
    private envDecayDisable: boolean = null;
    private envDecayLoopEnable: boolean = null;
    private lengthCounterEnable: boolean = null;
    private envReset: boolean = null;
    private shiftNow: boolean = null;

    public lengthCounter: number = null;
    public progTimerCount: number = null;
    public progTimerMax: number = null;
    private envDecayRate: number = null;
    private envDecayCounter: number = null;
    private envVolume: number = null;
    public masterVolume: number = null;
    public shiftReg: number = 1 << 14;
    public randomBit: number = null;
    public randomMode: number = null;
    public sampleValue: number = null;
    public accValue: number = 0;
    public accCount: number = 1;
    public tmp: number = null;

    constructor(papu: PAPU) {
        this.papu = papu;
        this.reset();
    }

    reset() {
        this.progTimerCount = 0;
        this.progTimerMax = 0;
        this.isEnabled = false;
        this.lengthCounter = 0;
        this.lengthCounterEnable = false;
        this.envDecayDisable = false;
        this.envDecayLoopEnable = false;
        this.shiftNow = false;
        this.envDecayRate = 0;
        this.envDecayCounter = 0;
        this.envVolume = 0;
        this.masterVolume = 0;
        this.shiftReg = 1;
        this.randomBit = 0;
        this.randomMode = 0;
        this.sampleValue = 0;
        this.tmp = 0;
    }

    clockLengthCounter() {
        if (this.lengthCounterEnable && this.lengthCounter > 0) {
            this.lengthCounter--;
            if (this.lengthCounter === 0) {
                this.updateSampleValue();
            }
        }
    }

    clockEnvDecay() {
        if (this.envReset) {
            // Reset envelope:
            this.envReset = false;
            this.envDecayCounter = this.envDecayRate + 1;
            this.envVolume = 0xF;
        }
        else if (--this.envDecayCounter <= 0) {
            // Normal handling:
            this.envDecayCounter = this.envDecayRate + 1;
            if (this.envVolume > 0) {
                this.envVolume--;
            }
            else {
                this.envVolume = this.envDecayLoopEnable ? 0xF : 0;
            }
        }
        this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume;
        this.updateSampleValue();
    }

    updateSampleValue() {
        if (this.isEnabled && this.lengthCounter > 0) {
            this.sampleValue = this.randomBit * this.masterVolume;
        }
    }

    writeReg(address: number, value: number) {
        if (address === 0x400C) {
            // Volume/Envelope decay:
            this.envDecayDisable = ((value & 0x10) !== 0);
            this.envDecayRate = value & 0xF;
            this.envDecayLoopEnable = ((value & 0x20) !== 0);
            this.lengthCounterEnable = ((value & 0x20) === 0);
            this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume;

        } else if (address === 0x400E) {
            // Programmable timer:
            this.progTimerMax = this.papu.getNoiseWaveLength(value & 0xF);
            this.randomMode = value >> 7;

        } else if (address === 0x400F) {
            // Length counter
            this.lengthCounter = this.papu.getLengthMax(value & 248);
            this.envReset = true;
        }
        // Update:
        //updateSampleValue();
    }

    setEnabled(value: boolean) {
        this.isEnabled = value;
        if (!value) {
            this.lengthCounter = 0;
        }
        this.updateSampleValue();
    }

    getLengthStatus() {
        return ((this.lengthCounter === 0 || !this.isEnabled) ? 0 : 1);
    }
}
